package server

import (
	"context"
	"crypto/md5"
	"fmt"
	"github.com/mitchellh/go-homedir"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/microsoft"
	"io"
	"log"
	"net/http"
	"net/url"
	"onedriver-storage/config"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

var (
	appDir string
)

const (
	endpoint = `https://graph.microsoft.com/v1.0`
)

func init() {
	dir, _ := homedir.Dir()
	appDir = filepath.Join(dir, ".oneDriverSync")
}

type Server struct {
	oauthConfig *oauth2.Config
	config      config.Config
	token       *oauth2.Token
	videos      []*Video

	mutex     sync.Mutex
	SyncIndex int

	OneDriver OneDriver
}

func NewServer(config config.Config) *Server {
	s := new(Server)
	s.oauthConfig = &oauth2.Config{
		RedirectURL:  config.Oauth2.RedirectURL,
		ClientID:     config.Oauth2.ClientId,
		ClientSecret: config.Oauth2.ClientSecret,
		Scopes:       config.Oauth2.Scopes,
		Endpoint:     microsoft.AzureADEndpoint(config.Oauth2.Tenant),
	}
	s.config = config
	return s
}

func (s *Server) getToken() (*oauth2.Token, error) {
	tk, err := s.oauthConfig.PasswordCredentialsToken(context.Background(), s.config.Oauth2.Username, s.config.Oauth2.Password)
	if err != nil {
		return nil, err
	}
	s.token = tk
	return tk, nil
}

func (s *Server) StartSYNC() error {

	_, err := s.getToken()
	if err != nil {
		return err
	}

	err = s.ReadVideo()
	if err != nil {
		return err
	}

	s.sync()
	return nil
}

func (s *Server) sync() {
	var (
		ch = make(chan *Video, s.config.Sync.GoRoutines)
	)

	for i := 0; i < s.config.Sync.GoRoutines; i ++ {
		go s.start(ch)
	}

	for i := 0; i < len(s.videos); i ++ {
		ch <- s.videos[i]
	}
}

func (s *Server) start(ch chan *Video) {
	tmpDir := filepath.Join(os.TempDir(), "onedriver_videos")
	os.MkdirAll(tmpDir, 0777)
	for v := range ch {
		var (
			thumbSavePath string
		)
		fname := strings.Replace(v.Name, " ", "", -1) + ".png"
		if ok, _ := s.findIfHasUploaded(fname); !ok {
			resp, err := http.Get(v.Thumb)
			if err != nil {
				log.Println("err -> ", err)
				continue
			}
			thumbSavePath = filepath.Join(tmpDir, fname)
			f, _ := os.Create(thumbSavePath)
			_, err = io.Copy(f, resp.Body)
			if err != nil {
				log.Println("err -> ", err)
				continue
			}
			f.Close()
			resp.Body.Close()
			log.Println("thumb save path:", thumbSavePath)
			err = s.uploadLittleFile(thumbSavePath)
		}
		// video.

		var (
			videoSavePath string
		)
		vname := strings.Replace(v.Name, " ", "", -1) + ".m3u8"
		vpath := getVideoOrigilalUrl(v.Url)
		if ok, _ := s.findIfHasUploaded(vname); !ok {
			resp, err := http.Get(v.Thumb)
			if err != nil {
				log.Println("err -> ", err)
				continue
			}
			videoSavePath = filepath.Join(tmpDir, fname)
			f, _ := os.Create(videoSavePath)
			log.Printf("downloading file: %s from %s\n", vname, vpath)
			_, err = io.Copy(f, resp.Body)
			if err != nil {
				log.Println("err -> ", err)
				f.Close()
				resp.Body.Close()

				continue
			}
			log.Printf("downloading file: %s ok, upload progress is doing\n", vname)
			f.Close()
			resp.Body.Close()

			log.Println("video save path:", videoSavePath)
			err = s.uploadLargeFile(videoSavePath)
			if err != nil {
				log.Printf("downloading file: %s ok, upload failed: %s\n", err)
			}
		}
	}
}

func smd5(str string) string {
	x := md5.New()
	x.Write([]byte(str))
	return fmt.Sprintf("%x", x.Sum(nil))
}

func getVideoOrigilalUrl(now string) string {
	url, _ := url.Parse(now)
	return url.Query().Get("url")
}
